Analiza danych pasażerow Titanica¶


RMS Titanic - jak zapewne wszyscy wiedzą, ale na wszelki wypadek przypomnę, zatonął o godzinie 02:20 w nocy z 14 na 15 kwietnia 1912 roku po kolizji z górą lodową na Atlantyku, podczas swego dziewiczego rejsu z Europy do Nowego Yorku. Z 2208 osob na pokładzie, ocalało tylko 712, z tego 6 zmarlo, zanim statki z ocalonymi dopłynęły do Nowego Yorku.

titanic i gora.jpg

Trasa statku¶

Początkowo rejs Titanica przebiegał bezproblemowo. Statek wypłynął o czasie z portu Southampton w Wielkiej Brytanii kierując się do Cherbourga we Francji. Ponieważ ten port był zbyt płytki aby przyjąć Titanica, więc pasażerów i ładunek dostarczono na pokład przy pomocy tendera (małego statku pomocniczego) S.S. "Normandic" (który nota bene do dziś zachowując niemal oryginalny wygląd, pływa po Sekwanie jako statek wycieczkowy). Następnie Titanic popłynął do Queenstown w Irlandii, skąd po zabraniu reszty pasażerów, 11 kwietnia rano wyruszył w rejs przez Atlantyk do Nowego Yorku.

TitanicRoute.jpg

Pasażerowie¶

Standardowy bilet III klasy kosztował miesięczną wypłatę robotnika wykwalifikowanego. Bilet II klasy - miesięczną wypłatę nauczyciela lub urzędnika. Zaś I klasy - miesięczną wypłatę lekarza lub inżyniera. Mówimy tu oczywiście o miesięcznych zarobkach w Stanach Zjednoczonych, w Europie pensje były kilkukrotnie niższe. W klasie I oprócz standardowych kabin, znajdowało się również kilka apartamentów o cenach przyprawiających o zawrót głowy. Korzystali z nich głównie milionerzy podróżujący ze swoimi służącymi, kamerdynerami i pokojówkami. Oczywiście za każdą osobę ze służby musieli uiścić opłatę jak za standardowy bilet I klasy.

Z tego też powodu, klasą I podróżowały głównie osoby bardzo zamożne i z tak zwanego "towarzystwa". Klasę II zajmowali drobni przemysłowcy, duchowni, prawnicy, dziennikarze, nauczyciele oraz turyści z Europy. W klasie III podróżowali praktycznie wyłącznie emigranci zarobkowi. Większość spośród nich stanowili Irlandczycy, Anglicy, Szwedzi, ale było także 50 Bułgarów, 37 Chowartów, 70 Libańczykow i 80 Syryjczyków, z których większość stanowili chrześcijanie uciekający z Imperium Osmańskiego przed prześladowaniami religijnymi.

Podczas całego rejsu, pasażerowie klasy III byli odseparowani i zamknięci w swojej części statku (choć dysponowali tam jadalnią i mieli własny pokład spacerowy na którym odbywały się tańce i spotkania towarzyskie). Nie było to widzimisię armatora, ale wymóg Amerykańskiego Urzędu Emigracyjnego, gdyż po dopłynięciu do USA, pasażerowie klasy I i II normalnie schodzili na ląd, natomiast pasażerowie klasy III byli przewożeni na wyspę Elis, gdzie robiono im zdjęcia, pobierano odciski palców i sprawdzano czy nie figurują w kartotekach poszukiwanych przestępców lub pacjentów szpitali psychiatrycznych. Sprawdzano też, czy nie mają gruźlicy, jaglicy (zakaźnej choroby oczu występującej do dziś w biednych krajach), czy nie są poligamistami lub anarchistami. Dopiero po takim screeningu mogli zacząć swoje nowe, piękne życie w USA.

Rozmieszczenie pasażerów na statku¶

titanic-przekroj.jpg

Pokłady statku oznaczono literami alfabetu, od najwyższego pokładu szalupowego, spełniającego głównie funkcję pokładu spacerowego, a w razie niebezpieczeństwa i ewakuacyjnego. Poprzez pokład A, gdzie mieściły się salony wypoczynkowe, biblioteka i palarnia klasy pierwszej. B i C w których znajdowały się kabiny klasy pierwszej i drugiej. D - jadalnie klasy pierwszej i drugiej. Oraz E, F i G przeznaczone głównie dla kabin klasy drugiej, trzeciej i pomieszczeń załogi. Niżej, częściowo już poniżej linii wody, mieściły się ładownie, kotłownie i maszynownia.

Na powyższym schemacie Titanica kolorem żółtym oznaczono pomieszczenia I klasy, kolorem zielonym - pomieszczenia II klasy, a na brązowo - pomieszczenia zajmowane przez pasażerow III klasy. Jak widać pasażerowie tej klasy byli rozdzieleni. Samotni mężczyzni zajmowali część dziobową statku. Zaś rodziny oraz samotne matki z dziećmi, płynące do swoich mężów, którzy wyemigrowali do USA już wcześniej i teraz byli w stanie opłacić swoim rodzinom podróż, aby te do nich dołączyły, zajmowały miejsce na rufie.

Wypadek¶

Feralnego dnia, 14 kwietnia, na kolację w pierwszej klasie podano Filet Migon (najdelikatniejszy stek cięty ze środkowej części polędwicy) z dodatkiem gęsiej wątróbki i karczochów, podlanych sosem truflowym. A do przepłukania gardła - sorbet z szampana i rumu. W trzeciej klasie były za to krakersy z serem i szynką. To i tak nieźle, bo na większości statków, pasażerowie trzeciej klasy na cały czas podróży musieli mieć własne jedzenie.

Gdy po kolacji pasażerowie poszli już spać, o godzinie 23:40 zauważono na wprost statku górę lodową. Statek skręcił ostro w lewo i pół minuty później otarł się o nią prawą burtą. Napierająca masa lodu powyginała stalowe płyty poszycia na długości ok. 90 metrów, naprężenia zerwały dziesiątki nitów utrzymujących razem blachy i woda zaczęła się wlewać do statku niewielkimi (niewielkimi, bo ich sumaryczna wielkość minimalnie przekraczała 1 metr kwadratowy) szczelinami do wnętrza kadłuba. Problem jednak polegał na tym, że woda zalewała na raz sześć pierwszych przedziałów, a statek mógł utrzymać się na powierzchni z zalanymi maksymalnie czterema.

Cross_section_of_the_Titanic.jpg

Ewakuacja pasażerów¶

Po zatrzymaniu silnikow i 40 minutowej inspekcji uszkodzeń przez kapitana wraz z cieślą okrętowym i głównym konstruktorem statku, inżynierem Thomasem Andrewsem, uznano że powstałych w kadłubie szczelin nie da się zatkać, a pompy nie poradzą sobie z napływającą do wnętrza wodą. Obliczono, że statek zatonie maksymalnie w ciągu dwóch godzin. Kapitan zarządził więc ewakuację. Ogłoszono alarm, obudzono pasażerów, rozdano wszystkim kamizelki ratunkowe i rozpoczęto opuszczanie szalup. Aby uspokoić zdenerwowanych pasażerów, orkiestra zaczęła grać. Pierwszą szalupę spuszczono o godzinie 00:40 (godzinę od momentu kolizji). Następne opuszczano już w odstępach pięciominutowych.

Ilość szalup ratunkowych¶

Po katastrofie formułowano zarzuty o zbyt małej ilości łodzi ratunkowych. Otóż Titanic dysponował następującą ilością środków ratunkowych: dwie małe łodzie, o numerach 1 i 2, które zwykle służyły do komunikacji z lądem i ewentualnego przewozu niewielkiej ilości ludzi lub ładunków - każda z nich mogła pomieścić maksymalnie 40 osob. 14 dużych łodzi wiosłowych, o numerach 2 - 16, z których każda mogła pomieścić 65 osob. Oraz 4 tratwy korkowe z podnoszonymi brezentowymi burtami, z których każda mogła pomieścić maksymalnie 47 osób. Owczesne przepisy uzależniały ilość łodzi nie od ilości pasażerów, ale od tonażu (wyporności) statku. I Titanic spełniał te normy z nawiązką.

Co więcej, jego projektanci uznali za praktycznie niemożliwe szybkie zatonięcie tak dużego statku i zaprojektowali środki ratunkowe na o wiele bardziej prawdopodobne zdarzenia, takie jak awaria maszyn na pełnym morzu czy zderzenie z innym, mniejszym statkiem. W jednym i drugim przypadku, gdyby dalsza kontynuacja podróży była niemożliwa, łodzie miały posłużyć jedynie do przewozu pasażerow pomiędzy Titaniciem, a statkami wysłanymi przez linię White Star w ciągu kilku lub najdalej kilkunastu godzin na miejsce zdarzenia. W takim przypadku ilość łodzi ratunkowych na Titanicu była aż nadto zadowalająca. Jednak w naszym, konkretnym przypadku starcia z górą lodową, ilość szalup okazała się tragicznie niewystarczająca.

Ilość ludzi w szalupach¶

To, że opuszczane do wody łodzie były nie do końca wypełnione ludźmi, wynikało z ówczesnych przepisów. Otóż łodzie, podczas opuszczania nie mogły być w pełni załadowane, aby nie zerwać żurawików z których były opuszczane. Resztę pasażerów, będąc już na wodzie, miały zabrać z otwieranych drzwi burtowych. O tym, że przepisy te miały sens, świadczy zdarzenie podczas opuszczania jednych z ostatnich łodzi, które były już maksymalnie wypełnione pasażerami. Wtedy to łódź numer 13 z 65 osobami na pokładzie (wg. "Encyclopedia Titanica", lub 55 osobami wg. Wikipedii), podczas opuszczania uszkodziła żurawiki na tyle, że przesuwające się w nich liny zacięły się i łódź zawisła prawie metr nad wodą. A ponieważ tuż obok niej spuszczana była łódź numer 15 z 68 osobami, o mały włos nie doszło do zderzenia tych łodzi i zmiażdżenia pasażerow. Aby tego uniknąć, w ostatniej chwili marynarze przecięli liny podtrzymujące pierwszą łódź, która z impetem spadła do wody. Komisje (zarówno angielska jak i amerykańska) badające potem wypadek, nie uznały działań oficerów nadzorujących opuszczanie łodzi za błąd. Na poniższej fotografii widać jak wygląda przechodzenie pasażerów przez drzwi burtowe do łodzi. To, że na Titanicu ich ostatecznie nie otworzono i nie wypełniono każdej ze znajdujących się na wodzie szalup do maksimum pasażerami, to już inna historia.

ladowanie pasazerow wg instrukcji.jpg

Fakt że łodzie, zwłaszcza te spuszczane jako pierwsze, nie były wypełnione w większym stopniu, wynikał również z tego, że pasażerowie nie bardzo kwapili się by opuścić spokojny i rozświetlony pokład największego i najbardziej luksusowego statku świata, tylko po to, aby w środku nocy przesiąść się do małej, drewnianej łupinki, chyboczącej się na wysokości ośmiu pięter ponad lodowatymi falami Atlantyku. Tym bardziej, że nie widać było żadnego realnego zagrożenia. Dlatego też niektóre panie musiano wręcz siłą wpychać do pierwszych szalup. Oczywiście sytuacja zmieniła się diametralnie po kilkudziesięciu minutach, gdy widać już było wyraźnie, że statek tonie, ale wtedy większość łodzi ratunkowych była już na wodzie.

Grafika pokazująca kolejne etapy tonięcia statku¶

Titanic_sinking_gif.gif

Jak widać, dopiero po upływie godziny od momentu kolizji można było dostrzec pierwsze, niepokojące oznaki, że statek tonie. Potem poszło już naprawdę szybko.


O Danych:¶

Zbiór danych zawiera informacje o pasażerach RMS Titanic. Dane obejmują takie atrybuty jak klasa podróży, wiek, płeć, liczba rodzeństwa/małżonków na pokładzie, liczba rodziców/dzieci na pokładzie, cena biletu oraz miejsce zaokrętowania. Zbiór zawiera także informacje o tym, czy dany pasażer przeżył katastrofe.

Kolumny:

  • pclass - Klasa biletu
  • survived - Czy pasażer przeżył katastrofę
  • name - Imię i nazwisko pasażera
  • sex - Płeć pasażera
  • age - Wiek pasażera
  • sibsp - Liczba rodzeństwa/małżonków na pokładzie
  • parch - Liczba rodziców/dzieci na pokładzie
  • ticket - Numer biletu
  • fare - Cena biletu
  • cabin - Numer kabiny
  • embarked - Port, w którym pasażer wszedł na pokład (C = Cherbourg, Q = Queenstown, S = Southampton)
  • boat - Numer łodzi ratunkowej
  • body - Numer ciała (jeśli pasażer nie przeżył i ciało zostało odnalezione)
  • home.dest - Miejsce docelowe
In [14]:
# sekcja importowa

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import matplotlib.patches as mpatches
from matplotlib.patches import Patch
import numpy as np
from scipy.interpolate import make_interp_spline
In [15]:
# wczytanie danych

df = pd.read_csv('26__titanic.csv', sep=",")

Pytania i hipotezy:¶

Patrząc na zbiór danych, od razu nasuwa się kilka pytań:

  1. Jaki wpływ na zwiększenie szansy przeżycia miał wiek? Może młodsi i bardziej sprawni mieli więcej szans na dostanie się do łodzi ratunkowych niż ludzie starsi, bardziej niedołężni?
  2. Czy płeć miała znaczenie? Może mężczyzni rycersko przepuszczali panie przodem?
  3. Czy znaczenie miała przynależność do danej klasy posażerów? A jeśli tak, to w jaki sposób?
  4. Czy posiadanie współmalżonka lub rodzeństwa na pokładzie zwiększało szanse na przeżycie? (Być może brat pomagał siostrze, a mąż żonie?)
  5. A może posiadanie dzieci lub rodziców zmniejszało szanse ocalenia? (Bo trzeba było się nimi dodatkowo zajmować, opiekować i pilnować?)
  6. Ciekawe, czy jest jakiś związek pomiędzy numerem szalupy, a klasą w której się podróżowało lub płcią, a może wiekiem? A jeśli tak, to jaki?
  7. Czy to, po której stronie statku (prawej lub lewej) czekało się na wejście do szalupy, miało jakiś wpływ na zwiększanie szans na ocalenie?

Postaram się znaleść odpowiedzi na te pytania podczas dalszej analizy.


1. Wnioski z analizy podstawowych informacji o danych:¶

Bingo! Mamy 1309 rekordów, czyli brakuje nam jedynie danych o 8 pasażerach (to dużo mniej niż 1%). Druga, jeszcze lepsza informacja jest taka, że mamy pełen komplet danych najbardziej nas interesujących, czyli o pasażerach, którzy przeżyli katastrofę. Jak bowiem wiemy, katastrofę przeżyło w sumie 712 osob, z tego 212 członków załogi, a my mamy dane 500 ocalonych pasażerow. Czyli pod tym względem nasze dane są kompletne i możemy na nich działać. Będziemy więc mogli odpowiedzieć na wszystkie postawione powyżej pytania i hipotezy.

Co prawda, u 15% ocalonych nie znany jest ich wiek, a w 5% nie znamy numeru szalupy do której się dostali, ale nie są to ilości, które zaważyłyby zdecydowanie na jakości naszej analizy. Znacznie gorzej jest w przypadku zajmowanej podczas rejsu kabiny. Tutaj brakujące dane stanowią przeszło 60% jeśli chodzi o ocalałych i aż 90% jeśli chodzi o zmarłych. Na szczeście akurat te dane nie będą nam potrzebne w naszych dalszych dociekaniach.

In [16]:
# kilka losowych rekordow, zeby zorientowac sie, z czym sie mierzymy
df.sample(5)
Out[16]:
pclass survived name sex age sibsp parch ticket fare cabin embarked boat body home.dest
961 3.0 0.0 Lennon, Miss. Mary female NaN 1.0 0.0 370371 15.5000 NaN Q NaN NaN NaN
856 3.0 1.0 Healy, Miss. Hanora "Nora" female NaN 0.0 0.0 370375 7.7500 NaN Q 16 NaN NaN
238 1.0 1.0 Robert, Mrs. Edward Scott (Elisabeth Walton Mc... female 43.0 0.0 1.0 24160 211.3375 B3 S 2 NaN St Louis, MO
1066 3.0 0.0 Novel, Mr. Mansouer male 28.5 0.0 0.0 2697 7.2292 NaN C NaN 181.0 NaN
301 1.0 0.0 Walker, Mr. William Anderson male 47.0 0.0 0.0 36967 34.0208 D46 S NaN NaN East Orange, NJ
In [17]:
# ile mamy w bazie osob ktore przezyly, a ile tych, ktore nie przezyly?
df['survived'].value_counts()
Out[17]:
0.0    809
1.0    500
Name: survived, dtype: int64
In [18]:
# jak rozkladaja sie brakujace dane w obu tych grupach (survived i nonsurvived)
df[df['survived'] == 1.0].isnull().sum()
Out[18]:
pclass         0
survived       0
name           0
sex            0
age           73
sibsp          0
parch          0
ticket         0
fare           0
cabin        307
embarked       2
boat          23
body         500
home.dest    153
dtype: int64
In [19]:
df[df['survived'] == 0.0].isnull().sum()
Out[19]:
pclass         0
survived       0
name           0
sex            0
age          190
sibsp          0
parch          0
ticket         0
fare           1
cabin        707
embarked       0
boat         800
body         688
home.dest    411
dtype: int64

2. Wnioski z analizy pojedynczych zmiennych:¶

Ze względu na sposób rozmieszczenia pasażerow na statku, trzeba rozpatrywać każdą klasę pasażerów osobno. Rzut oka na poniższe wykresy pokazuje nam jasno, że kobiety i dzieci z I i II klasy zostały uratowane prawie w 100%. Z tego, co pamiętam, jedyne nie uratowane dziecko z klasy I to 2-letnia dziewczynka, która zgubiła się gdzieś swoim rodzicom podczas przechodzenia na pokład szalupowy. Rodzice poszli jej szukać i cała trójka przepadła. Kilka kobiet nie chciało opuścić swoich mężów, a jedna ukochanego psa i została potem odnaleziona zamarznięta w wodzie nadal trzymając w ramionach swojego równie zamarzniętego pupila. Ale są to jednostkowe przypadki. Generalnie, bycie kobietą lub dzieckiem do 15 roku życia z klasy I lub II praktycznie gwarantowało ocalenie. Inaczej przedstawiała się sytuacja w klasie III. Tutaj szanse na uratowanie w przypadku bycia kobietą lub dzieckiem oscylowały w granicach 50%.

Jeśli natomiast chodzi o mężczyzn, to mamy tutaj prawdziwą hekatombę. Przeżyło ich niewielu. Choć widać pewne prawidłowości. Po pierwsze, procentowy udział ocalonych był tym wyższy, im do wyższej klasy należeli. Po drugie, najwięcej ocalało młodych mężczyzn do 35 roku życia. Oprócz I klasy, starsi ocaleni mężczyźni to jedynie pojedyncze przypadki. W ten sposób uzyskaliśmy odpowiedzi na nasze pierwsze trzy pytania:

1. Jaki wpływ na zwiększenie szansy przeżycia miał wiek?¶

Bardzo duży. Jeśli chodzi o mężczyzn, to ocaleli przede wszystkim młodzi. We wszystkich klasach średnia wieku ocalonych była znacząco niższa niż średnia wieku pasażerów przed katastrofą. W przypadku II klasy średnia ta spadła nawet z 31 do 17 lat.

2. Czy płeć miała znaczenie?¶

Decydujące! Przeżyło przeszło 95% kobiet z I klasy, prawie 90% z klasy II i już tylko połowa kobiet z III klasy. W przypadku mężczyzn, przeżyło tylko 35% z I klasy i po 15% z klasy II i III. Wciąż jednak nie wiemy: dlaczego? Odpowiedzią na to pytanie zajmiemy się za chwilę.

3. Czy znaczenie miała przynależność do danej klasy?¶

Oczywiście, jak najbardziej. Ocalało 62% pasażerów z I klasy, 43% z II klasy i już tylko 25% z III klasy.

4. Czy posiadanie małżonka lub rodzeństwa zwiększa szanse na przeżycie?¶

Obliczając spomiędzy pasażerów posiadających rodzeństwo lub współmałżonka, procent tych którzy zostali uratowani, dochodzimy do wniosku, że jest ich troszkę więcej niż dla ogołu populacji pasażerow. I tak w klasie I jest to 71% do 62%, w II klasie 53% do 43%, a w III klasie 26% do 25%. Oznacza to, że posiadanie małżonka lub rodzeństwa zwiększa, choć jedynie o kilka-kilkanaście procent szanse na przeżycie. Tym niemniej, lepsze takie zwiększenie szans niż żadne. Choć muszę przyznać, że spodziewałem się bardziej spektakularnych wyników. Niniejszym hipoteza z punktu 4 zostala, choć "z pewną taką nieśmiałością", to jednak potwierdzona.

5. Czy posiadanie dzieci lub rodziców zmniejsza szanse na ocalenie?¶

Absolutnie nie! W przypadku posiadania rodziców lub dzieci, czyli osób pod naszą opieką, nasze szanse na ocalenie paradoksalnie rosną. I proporcje te wynoszą odpowiednio 73% do 62% w I klasie, 77% do 43% w II klasie i 32% do 25% w klasie III. Co oznacza, że posiadanie dzieci lub rodziców nie tylko, że nie było obciażeniem, ale znacząco zwiększyło szanse na ocalenie, bo od 20 do aż 80%! Oznacza to całkowite obalenie hipotezy zawartej w punkcie 5.

7. Czy to, po której stronie statku (prawej lub lewej) czekało się na wejście do szalupy, miało jakiś wpływ na zwiększanie szans na ocalenie?¶

Okazuje się, że tak! Z lewej burty do szalup weszło jedynie 216 osób, podczas gdy z prawej burty aż 251, czyli o przeszło 15% wiecej. Czyli tak z pozoru błaha i nieistotna sprawa jak to, z której strony statku próbowaliśmy się dostać do szalup, może poważnie zwiększyć lub zmniejszyć nasze szanse na ratunek. W większym nawet stopniu niż posiadanie uczynnego meża czy pomocnego brata. Dowiedzmy się zatem, dlaczego tak się stało?

In [20]:
# ocaleni w podziale na plec i klase (w procentach)
# -------------------------------------------------

# grupowanie danych wedlug klasy i plci
grouped = df.groupby(['pclass', 'sex'])

# obliczanie liczby wszystkich pasazerow w kazdej grupie
total_counts = grouped.size().reset_index(name='total')

# obliczanie liczby osob, ktore przezyly w kazdej grupie
survived_counts = grouped['survived'].sum().reset_index(name='survived')

# zlaczenie obliczonych danych
merged = pd.merge(total_counts, survived_counts, on=['pclass', 'sex'])

# obliczanie procentowego udzialu osob, ktore przezyly
merged['survival_rate'] = ((merged['survived'] / merged['total']) * 100).round(1)

# tworzenie DataFrame dla sumowania wierszy Total
totals = merged.groupby('pclass').sum(numeric_only=True).reset_index()
totals['sex'] = 'Total'
totals['survival_rate'] = ((totals['survived'] / totals['total']) * 100).round(1)

# dodawanie linii sumujacych dla kazdej klasy
final_df = pd.concat([merged, totals], ignore_index=True)

# sortowanie wedlug klasy i plci (tak, zeby "Total" pojawialo sie na koncu kazdej klasy)
final_df['sex'] = pd.Categorical(final_df['sex'], categories=['female', 'male', 'Total'], ordered=True)
final_df = final_df.sort_values(by=['pclass', 'sex']).reset_index(drop=True)

# i voila!
final_df
Out[20]:
pclass sex total survived survival_rate
0 1.0 female 144 139.0 96.5
1 1.0 male 179 61.0 34.1
2 1.0 Total 323 200.0 61.9
3 2.0 female 106 94.0 88.7
4 2.0 male 171 25.0 14.6
5 2.0 Total 277 119.0 43.0
6 3.0 female 216 106.0 49.1
7 3.0 male 493 75.0 15.2
8 3.0 Total 709 181.0 25.5
In [22]:
# statystyki wieku dla mezczyz (poczatkowe i po ocaleniu)
# -------------------------------------------------------

# filtrowanie danych: wszyscy mezczyzni
all_males = df[df['sex'] == 'male']

# filtrowanie danych: tylko mezczyzni ktorzy przezyli
survived_males = all_males[all_males['survived'] == 1.0]

# funkcja liczaca statystyki z zaokragleniem
def compute_stats(grouped):
    stats = grouped.agg(['mean', 'std', 'median', lambda x: x.quantile(0.25), lambda x: x.quantile(0.75)])
    stats.columns = ['mean', 'std', 'median', 'Q1', 'Q3']
    return stats.round()

# grupowanie danych wg klas i obliczanie statystyk
statistics_all = compute_stats(all_males.groupby('pclass')['age'])
statistics_survived = compute_stats(survived_males.groupby('pclass')['age'])

# i wynik
print("Statystyki dla wszystkich mężczyzn:")
print(statistics_all.to_string(index=True))
print("\nStatystyki dla mężczyzn, którzy przeżyli:")
print(statistics_survived.to_string(index=True))
Statystyki dla wszystkich mężczyzn:
        mean   std  median    Q1    Q3
pclass                                
1.0     41.0  15.0    42.0  30.0  50.0
2.0     31.0  14.0    30.0  23.0  39.0
3.0     26.0  12.0    25.0  20.0  32.0

Statystyki dla mężczyzn, którzy przeżyli:
        mean   std  median    Q1    Q3
pclass                                
1.0     36.0  15.0    36.0  27.0  48.0
2.0     17.0  17.0    19.0   2.0  30.0
3.0     22.0  11.0    25.0  18.0  29.0
In [23]:
# rysowanie wyrabistych wykresow
#--------------------------------

# ustawienia stylu wykresow
sns.set(style="whitegrid")

# tworzenie subplotow dla wszystkich wykresow
fig, axs = plt.subplots(3, 2, figsize=(18, 15), sharey=False)

# kolory dla kazdej z klas
colors = {
    1.0: {'all': 'darkgray', 'survived': 'blue'},
    2.0: {'all': 'darkgray', 'survived': 'green'},
    3.0: {'all': 'darkgray', 'survived': 'red'}
}

# dodanie odstepu miedzy wykresami
plt.subplots_adjust(wspace=0.05, hspace=0.5)

# tworzenie wykresow dla kobiet oraz mezczyzn w poszczegolnych klasach
for i, pclass in enumerate([1.0, 2.0, 3.0]):
    df_class = df[df['pclass'] == pclass]
    
    ax_female = axs[i, 0]
    ax_male = axs[i, 1]

    females = df_class[df_class['sex'] == 'female']
    males = df_class[df_class['sex'] == 'male']
    
    # histogram dla kobiet
    all_females = females['age']
    survived_females = females[females['survived'] == 1.0]['age']
    
    ax_female.hist(all_females.dropna(), bins=range(0, 81, 5), color=colors[pclass]['all'], label='Kobiety', alpha=0.7, width=4)
    ax_female.hist(survived_females.dropna(), bins=range(0, 81, 5), color=colors[pclass]['survived'], label='Kobiety które przeżyły', alpha=0.7, width=4)
    ax_female.set_title(f'Klasa {int(pclass)} Kobiety', fontsize=16)
    ax_female.set_xlabel('Wiek (w latach)', fontsize=16)
    ax_female.set_ylabel('Ilość pasażerów', fontsize=16)
    
    if i < 2:  # zmieniamy skalę pionowa na 30 dla dwoch pierwszych rzedow
        ax_female.set_ylim(0, 30)
    elif i == 2:  # zmieniamy skale dla trzeciego rzedu dla kobiet na 40
        ax_female.set_ylim(0, 40)
    ax_female.legend(fontsize=14)

    # histogram dla mezczyzn
    all_males = males['age']
    survived_males = males[males['survived'] == 1.0]['age']
    
    ax_male.hist(all_males.dropna(), bins=range(0, 81, 5), color=colors[pclass]['all'], label='Mężczyźni', alpha=0.7, width=4)
    ax_male.hist(survived_males.dropna(), bins=range(0, 81, 5), color=colors[pclass]['survived'], label='Mężczyźni którzy przeżyli', alpha=0.7, width=4)
    ax_male.set_title(f'Klasa {int(pclass)} Mężczyźni', fontsize=16)
    ax_male.set_xlabel('Wiek (w latach)', fontsize=16)
    ax_male.set_ylabel('Ilość pasażerów', fontsize=16)
    
    if i < 2:  # zmieniamy skale pionowa na 30 dla dwoch pierwszych rzedow
        ax_male.set_ylim(0, 30)
    elif i == 2:  # zostawiamy wyzsza skale dla trzeciego rzedu na 90
        ax_male.set_ylim(0, 90)
    ax_male.legend(fontsize=14)

    # dodanie poziomych linii na wszystkich wykresach
    ax_female.yaxis.grid(True, linestyle='-', which='both', color='grey', alpha=0.7)
    ax_male.yaxis.grid(True, linestyle='-', which='both', color='grey', alpha=0.7)

plt.tight_layout(rect=[0, 0, 1, 0.95])
plt.show()
No description has been provided for this image
In [24]:
# liczenie szans na przezycie dzieciatych i mezatych
# --------------------------------------------------

# funkcja obliczanie procentow osob ktore przezyly do tych ktore nie przezyly
def calculate_percentage(survived_count, non_survived_count):
    total = survived_count + non_survived_count
    survived_percentage = ((survived_count / total) * 100)
    non_survived_percentage = ((non_survived_count / total) * 100)
    return survived_percentage, non_survived_percentage

# inicjalizacja slownikow do przechowywania rezultatow
sibs_results = {}
parch_results = {}

# zdefiniowanie listy klas
classes = [1.0, 2.0, 3.0]

# Loop
for cls in classes:
    survived_sibs = df[(df['survived'] == 1.0) & (df['sibsp'] > 0) & (df['pclass'] == cls)].shape[0]
    non_survived_sibs = df[(df['survived'] == 0.0) & (df['sibsp'] > 0) & (df['pclass'] == cls)].shape[0]
    sibs_results[cls] = calculate_percentage(survived_sibs, non_survived_sibs)
    
    survived_parch = df[(df['survived'] == 1.0) & (df['parch'] > 0) & (df['pclass'] == cls)].shape[0]
    non_survived_parch = df[(df['survived'] == 0.0) & (df['parch'] > 0) & (df['pclass'] == cls)].shape[0]
    parch_results[cls] = calculate_percentage(survived_parch, non_survived_parch)

# i wyniczki
print("\nPasażerowie z rodzeństwem lub współmałżonkiem:\n")
for cls, res in sibs_results.items():
    print(f"Klasa {cls}: Survived {round(res[0])}%, Not Survived {round(res[1])}%")

print("\nPasażerowie z dziećmi bądź rodzicami:\n")
for cls, res in parch_results.items():
    print(f"Klasa {cls}: Survived {round(res[0])}%, Not Survived {round(res[1])}%")
Pasażerowie z rodzeństwem lub współmałżonkiem:

Klasa 1.0: Survived 71%, Not Survived 29%
Klasa 2.0: Survived 53%, Not Survived 47%
Klasa 3.0: Survived 26%, Not Survived 74%

Pasażerowie z dziećmi bądź rodzicami:

Klasa 1.0: Survived 73%, Not Survived 27%
Klasa 2.0: Survived 77%, Not Survived 23%
Klasa 3.0: Survived 32%, Not Survived 68%
In [25]:
# dodanie kolumny 'burta': prawa i lewa
#-------------------------------------

# funkcja okreslajaca wartosci dla nowej kolumny 'burta'
def determine_burta(boat):
    if boat in ['2', '4', '6', '8', '10', '12', '14', '16', 'B', 'D']:
        return 'L'
    elif boat in ['1', '3', '5', '7', '9', '11', '13', '15', 'A', 'C']:
        return 'P'
    else:
        return None

# Dodawanie nowej kolumny 'burta' do bazy danych
df['burta'] = df['boat'].apply(determine_burta)
In [26]:
# ocaleni z lewej burty
survived_left_df = df[(df['survived'] == 1.0) & (df['burta'] == 'L')].shape[0]
print(survived_left_df)
216
In [27]:
# ocaleni z prawej burty
survived_right_df = df[(df['survived'] == 1.0) & (df['burta'] == 'P')].shape[0]
survived_right_df
Out[27]:
251

3. Wnioski z analizy demograficznej poszczególnych szalup:¶

Do tej pory rozpatrywaliśmy nasze dane w sposob statyczny. Tyle a tyle osób uratowanych, tyle a tyle utonęło. Jednak pasażerowie nie zostali przecież uratowani w mgnieniu oka. To był rozciągnięty w czasie proces. Szalupy były kolejno wypełniane ludźmi i spuszczane na wodę. Minuta po minucie, godzina po godzinie. Ciekawe do jakich wniosków dojdziemy, jeśli będziemy rozpatrywać proces ratowania pasażerów jako charakteryzujący się pewną dynamiką, zmieniający się w czasie.

Wyobraźmy to sobie. Z każdą chwilą Titanic coraz bardziej się zanurza. Pokład już nie jest zupełnie poziomy, ale przechyla się w stronę pogrążonego w wodzie dziobu. Już na pewno wiadomo, że statek zatonie. Zimne powietrze przeszywa od czasu do czasu przeraźliwy gwizd pary spuszczanej z kotłów, żeby nie wybuchły w zetknięciu z lodowatą wodą. Deski pokładu skrzypią pod naporem setek nóg. Wzrasta napięcie. Marynarze coraz szybciej spuszczają na wodę kolejne szalupy. Niektórzy pasażerowie głośno się modlą, ktoś inny klnie na czym świat stoi. Orkiestra gra coraz bardziej smutne i pobożne pieśni. Duchowni spowiadają ludzi i intonują modlitwy. Wszak wszyscy zaraz staniemy przed obliczem Pana. Niektórzy pasażerowie w spokoju godzą się z losem, inni rozpaczliwie szukają ratunku. Groźba zbliżającej się śmierci z każdą minutą staje się coraz bardziej realna. Ludzi zaczyna ogarniać panika. W takich okolicznościach ujawniają się najlepsze i najgorsze cechy naszego charakteru.

6. Czy jest jakiś związek pomiędzy numerem szalupy, a klasą lub płcią?¶

Patrząc na poniższe wykresy, widzimy wyraźnie jak zmieniał się w czasie skład osobowy pasażerów umieszczanych w szalupach. W pierwszych siedmiu, które zostały zwodowane, znajdują się niemal wyłącznie w 100% przedstawiciele klasy I. Następnie mamy łodź nr 16, która jest silną anomalią jako wypełniona jedynie przedstawicielami, a raczej przedstawicielkami klasy III. W następnych czterech łodziach za to przeważają zdecydowanie przedstawiciele klasy II, z niewielkimi, kilkuosobowymi dopełnieniami z pozostałych klas. W kolejnych łodziach zaczynają już górować osoby z klasy III. Prawdopodobnie na skutek tego, że "skończyły się" kobiety i dzieci z klas wyższych. Przez "skończyły się" rozumiem oczywiście, że nie było ich już więcej na pokładzie ewakuacyjnym. Ostatnich kilka łodzi jest już wypełnionych dość losowo, z dominującą jednak grupą klasy III. Rozkład klasowy jest świetnie widoczny zwłaszcza w przypadku łodzi opuszczanych z prawej burty. Dowodcą odpowiedzialnym za wypełnianie i opuszczanie szalup po tej stronie był pierwszy oficer Murdoch. Jak widać mocno przestrzegał on porządku, a przy tym uratował kilkudziesięciu pasażerów więcej niż dowodzący po lewej stronie, drugi oficer Lightoller. Licząc również członków załogi, Murdoch ocalił przeszło 100 osób więcej. Jak więc widać, porządek podczas ewakuacji daje plus jeden do ratunku.

Ciekawą rzecz pokazują nam wykresy dotyczące płci uratowanych osób w podziale na szalupy. Widać tutaj ogromną dysproporcję pomiędzy szalupami opuszczanymi z prawej, a tymi z lewej burty. Wynikało to z faktu różnej interpretacji rozkazu kapitana Smitha o ratowaniu kobiet i dzieci. Drugi oficer Lightoller, który nadzorował opuszczanie szalup z lewej strony (backburty), zinterpretował polecenie kapitana jako: TYLKO kobiety i dzieci, podczas gdy pierwszy oficer Murdoch, kierujący opuszczaniem szalup z prawej strony (sterburty) jako: NAJPIERW kobiety i dzieci. Przez co w "jego" szalupach wolne miejsca były zajmowane również przez mężczyzn. To właśnie dzięki temu, ostatecznie ocalił więcej osób. Czyli to nie ślepe wykonywanie rozkazów, ale elastyczne ich dopasowanie do zmieniających się okoliczności, zwiększa szanse na ocalenie.

Analiza składu osobowego kolejno wodowanych łodzi ratunkowych.¶

Schemat rozmieszczenia szalup na pokładzie¶

rozmieszczenie lodzi ratunkowych.jpg

Czasy wodowania poszczególnych szalup:¶

Czas zwodowania Numer szalupy Pojemnosc standardowa Pojemnosc w momencie opuszczania
00:40 7 65 28
00:45 5 65 36
00:55 3 65 32
01:00 8 65 25
01:05 1 40 12
01:10 6 65 22
01:20 16 65 53
01:25 14 65 40
01:30 9 65 40
01:30 12 65 42
01:35 11 65 50
01:35 13 65 55
01:40 15 65 68
01:45 2 40 17
01:50 10 65 57
01:50 4 65 30
02:00 C 47 43
02:05 D 47 20
02:14 A 47 12
02:15 B 47 12
------------------------------------------------------------------ ------------------------------------------------------------------ ------------------------------------------------------------------ ------------------------------------------------------------------
Calkowite ilosci: 20 1178 686

Ostatnie opuszczane tratwy, o oznaczeniach A i B nie zostały już normalnie zwodowane, ale po prostu zmyte przez fale z pokładu zanurzającego się Titanica. Tratwa A nabrała dużo wody i pasażerowie, którym udało się do niej dopłynąć i wejść, musieli stać po kolana w wodzie. Przeszło połowa z nich zmarła z wyziębienia. Tratwa B z kolei przewróciła się do góry dnem i tylko kilkanaście osób zdołało się na nią wdrapać i jakoś utrzymać. Był między nimi drugi oficer Lightoller, który dysponował gwizdkiem. To ich uratowało, gdyż właśnie jego gwizdek usłyszały ekipy ratunkowe, które już zawracały uznając, że nie znajdą więcej żywych rozbitków. Jak widać gwizdek daje plus jeden do ratunku.

Musimy do kolejności opuszczanych łodzi wprowadzić pewną korektę. Mianowicie łódź nr 4, choć zaczęła być opuszczana jako jedna z pierwszych, jeszcze przed łodzią nr 6, została zatrzymana na poziomie pokładu A, skąd zabierała jeszcze pasażerów. Ostatecznie więc została zwodowana jako jedna z ostatnich. Z uwagi na to, że ważniejszy jest dla nas sam moment wchodzenia pasażerów do łodzi, a nie jej kontakt z wodą, uznałem, że taka zmiana ma sens.

Niech nas nie zwiedzie również absurdalnie wręcz małe wypełnienie niektórych łodzi, jak na przykład szalupy nr 1. Nie zapominajmy, że oprócz pasażerów uratowało się także 212 członków załogi. Musieli się oni gdzieś pomieścić. Do każdej z szalup standardowo przydzielano po 4 marynarzy do obsługi wioseł i jednego podoficera bądź młodszego oficera jako dowódcę łodzi. Akurat w przypadku łodzi nr 1 zabrała ona dodatkowo 10 palaczy (oczywiście nie papierosów, tylko obsługi kotłów parowych), którzy niemiłosiernie usmarowani właśnie wydostali się na pokład ewakuacyjny szybem wentylacyjnym uciekając z zalewanej przez wodę kotłowni.

In [34]:
# czadowe wykresy pokazujace rozklad klas w poszczegolnych lodziach
# -----------------------------------------------------------------

# funkcja obliczajaca liczbe pasazerow dla kazdej klasy w kazdej z lodzi
def count_passengers_by_class(df, boat_order):
    results = []
    for boat in boat_order:
        for cls in [1.0, 2.0, 3.0]:
            count = len(df[(df['boat'] == boat) & (df['pclass'] == cls)])
            results.append({'boat': boat, 'class': cls, 'count': count})
    return pd.DataFrame(results)

# kolejnosc wodowania szalup
boat_order = ['7', '5', '3', '8', '1', '4', '6', '16', '14', '9', '12', '11', '13', '15', '2', '10', 'C', 'D', 'A', 'B']

# liczenie pasazerow wedlug klasy i lodzi
data = count_passengers_by_class(df, boat_order)

# filtracja danych na prawa i lewa burte
data_right = data[data['boat'].isin([boat for boat in boat_order if determine_burta(boat) == 'P'])]
data_left = data[data['boat'].isin([boat for boat in boat_order if determine_burta(boat) == 'L'])]

# tworzenie subplots
fig = plt.figure(figsize=(18, 12))
gs = fig.add_gridspec(2, 2, width_ratios=[1, 1], height_ratios=[1, 1])

# gorny sumaryczny wykres na caly pierwszy rząd (obie kolumny)
ax_big = fig.add_subplot(gs[0, :])
# dwa ponizsze
ax_right = fig.add_subplot(gs[1, 1])  # prawy dolny
ax_left = fig.add_subplot(gs[1, 0])   # lewy dolny

# Funkcja
def plot_barchart(data, ax, title, show_ylabel=False):
    sns.barplot(x='boat', y='count', hue='class', data=data, 
                palette={1.0: 'blue', 2.0: 'green', 3.0: 'red'}, 
                ax=ax, legend=False)
    ax.set_title(title)
    ax.set_ylabel('Liczba pasażerów' if show_ylabel else '')
    ax.set_xlabel('Numer szalupy')
    for label in ax.get_xticklabels():
        label.set_fontweight('bold')
    ax.grid(False, axis='x')
    ax.grid(True, axis='y', linestyle=':', linewidth=1, color='black', alpha=0.5)

plot_barchart(data, ax_big, 'Ilość pasażerów w kolejnych wodowanych łodziach w podziale na klasy', show_ylabel=True)
plot_barchart(data_right, ax_right, 'Tylko z prawej burty')
plot_barchart(data_left, ax_left, 'Tylko z lewej burty')

# Synchronizacja osi Y
for ax in [axes[0,1], axes[1,0]]:
    ax.set_yticks(axes[0,0].get_yticks())
    ax.set_ylabel('')

legend_patches = [
    Patch(facecolor='blue', label='Klasa 1'),
    Patch(facecolor='green', label='Klasa 2'),
    Patch(facecolor='red', label='Klasa 3')
]
fig.legend(handles=legend_patches, loc='upper center', ncol=3, title='Legenda', bbox_to_anchor=(0.5, 0.93))

plt.tight_layout()
plt.show()
No description has been provided for this image
In [40]:
# wyrabista funkcja pokazujaca rozklad plci i dzieci w poszczegolnych lodziach
# ----------------------------------------------------------------------------

# funkcja obliczajaca liczbe pasazerow wedlug plci i wieku w kazdej z lodzi
def count_passengers_by_sex_and_age(df, boat_order):
    results = []
    for boat in boat_order:
        for sex in ['female', 'male']:
            total_count = len(df[(df['boat'] == boat) & (df['sex'] == sex)])
            children_count = len(df[(df['boat'] == boat) & (df['sex'] == sex) & (df['age'] < 15)])
            results.append({'boat': boat, 'sex': sex, 'total_count': total_count, 'children_count': children_count})
    return pd.DataFrame(results)

# liczenie pasazerow wedlug plci i wieku
data_sex_age = count_passengers_by_sex_and_age(df, boat_order)

# filtracja danych na prawej i lewej burcie
data_right_sex = data_sex_age[data_sex_age['boat'].isin([boat for boat in boat_order if determine_burta(boat) == 'P'])]
data_left_sex = data_sex_age[data_sex_age['boat'].isin([boat for boat in boat_order if determine_burta(boat) == 'L'])]

# tworzenie subplots
fig = plt.figure(figsize=(18, 12))
gs = fig.add_gridspec(2, 2, width_ratios=[1, 1], height_ratios=[1, 1])

# gorny sumaryczny wykres na caly pierwszy rząd (obie kolumny)
ax_big = fig.add_subplot(gs[0, :])
# dwa ponizsze
ax_right = fig.add_subplot(gs[1, 1])  # prawy dolny
ax_left = fig.add_subplot(gs[1, 0])   # lewy dolny

# funkcja do rysowania wykresow
def plot_barchart_by_sex(data, ax, title):
    colors = {'female': 'deeppink', 'male': 'deepskyblue'}
    bar_width = 0.4
    boats = data['boat'].unique()

    for sex in ['female', 'male']:
        sex_data = data[data['sex'] == sex]
        x = [i + (-bar_width/2 if sex == 'female' else bar_width/2) for i in range(len(boats))]
        ax.bar(x, sex_data['total_count'], bar_width, color=colors[sex], label='Kobiety' if sex == 'female' else 'Mężczyźni')
        for index, row in enumerate(sex_data.iterrows()):
            ax.bar(x[index], row[1]['children_count'], bar_width, color=colors[sex], hatch='//', edgecolor='black')

    ax.set_title(title)
    ax.set_ylabel('Liczba pasażerów' if ax is axes[0] else '')
    ax.set_xlabel('Numer szalupy') 
    ax.set_xticks(range(len(boats)))
    ax.set_xticklabels(boats, fontweight='bold')
    ax.grid(False, axis='x')
    ax.grid(True, axis='y', linestyle=':', linewidth=1, color='black', alpha=0.5)
    
plot_barchart_by_sex(data_sex_age, ax_big, 'Ilość pasażerów w kolejnych wodowanych łodziach w podziale na płeć')
plot_barchart_by_sex(data_right_sex, ax_right, 'Tylko z prawej burty')
plot_barchart_by_sex(data_left_sex, ax_left, 'Tylko z lewej burty')

axes = [ax_big, ax_left, ax_right]  # dla dalszych operacji, np. y-ticks

# ustawienia osi Y
for ax in axes[1:]:
    ax.set_yticks(axes[0].get_yticks())
    ax.set_ylabel('')

# dodanie legendy
handles, labels = axes[0].get_legend_handles_labels()
handles.append(Patch(facecolor='white', hatch='//', edgecolor='black', label='Dzieci'))
fig.legend(handles=[handles[0], handles[1], handles[-1]], labels=['Kobiety', 'Mężczyźni', 'Dzieci'], loc='upper center', ncol=1, title='Legenda', bbox_to_anchor=(0.5, 0.93))

plt.tight_layout()
plt.show()
No description has been provided for this image

4. Podsumowanie¶

W toku analizy odpowiedzieliśmy sobie na wszystkie postawione na początku pytania i hipotezy. Wiemy już, że bycie kobietą lub dzieckiem w klasie I i II dawało ogromne, prawie stuprocentowe szanse na przyżycie. Wynikało to oczywiście z ówczesnych norm społecznych, w których to kobietom, jako słabej płci, należało bezwzględnie ustępować miejsca, przepuszczać przodem czy wstawać, gdy podchodziły do stołu. Ciekawe, czy w dzisiejszych czasach, panowie nadal zachowaliby się równie szarmancko? Zwłaszcza gdyby w grę wchodziło ich życie, a nie tylko zwykła kurtuazja. Co prawda, nawet na Titanicu oficerowie nadzorujący ewakuację pasażerów, musieli kilkukrotnie użyć broni, by wymusić porządek i odpędzić mężczyzn z "dołów społecznych" przed wtargnięciem do szalup, gdy wsiadały do nich panie. Ciekawe również, czy na współczesnych, dużych liniowcach, w przypadku ataku piratów, zamachu terrorystycznego czy ostrzału rakietowego przez Huttich, bilet pierwszej klasy nadal byłby przepustką do ocalenia? A zresztą nawet jeśli nie, to i tak pewnie chyba lepiej być bogatym :-)

Wracając jeszcze do Titanica, tak wielu pasażerów III klasy zginęło, gdyż nie byli w stanie po prostu wydostać się na pokład. Stewardzi, którzy jako jedyni posiadali klucze do krat odgradzających pasażerów III klasy od reszty statku, byli mocno zajęci budzeniem pasażerów wyższych klas, rozdawaniem im kamizelek ratunkowych, kierowaniem na pokład ewakuacyjny i do kolejnych szalup, szukaniem zagubionych dzieci, itp. Skutkiem tego, jak wynika to z licznych relacji ocalałych, część krat do samego końca pozostała zamknięta. Jeżeli już więc w końcu pasażerom III klasy jakoś udało się wydostać z wnętrza statku i w plątaninie zalewanych przez wodę korytarzy trafić na najwyżej położony pokład ewakuacyjny, to i tak większość szalup już odpłynęła.

Na koniec mała ciekawostka: otóż film "Titanic" z 1997 roku w reżyserii Jamesa Camerona z Leonardo di Caprio i Kate Winslet w rolach głównych, był droższy w produkcji (w przeliczeniu na dzisiejsze pieniądze) niż wybudowanie w 1911 roku samego Titanica".

In [ ]: